//*********************************************************************
//		Copyright (c) 2003 - 2008 Microsoft Corporation.
//      This code is provided under Microsoft Public License, which can be found at
//      http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx 
//
// @File: DbInstaller.cs
//
// Purpose: Repository installation & configuration functions
//
//
//
// Notes: 
//
//
//	
// History: 
//     @Version: V2.1
//
//     08/18/08 PGV 2.1.001.0 Starting V2.1 (based on 2.0.030.0 code)
//
// @EndHeader@
//*********************************************************************

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.OleDb;
using System.IO;
using System.Text;
using System.Collections;


namespace H2V2.ConfigUtility
{
	public struct st_config 
	{
		public string description;
		public string cfg_value;
	}

	public class StatusChangeEventArgs : EventArgs 
	{
		public string m_text;
		public bool m_IsException = false;
		public Exception m_exception;

		public StatusChangeEventArgs (string a_text) {m_text = a_text;}
		public StatusChangeEventArgs (Exception a_ex)
		{
			m_IsException = true;
			m_exception = a_ex;
		}
	}

	public class DbInstaller
	{
        public static readonly Version Version = new Version (((System.Reflection.AssemblyFileVersionAttribute)
            (System.Reflection.Assembly.GetExecutingAssembly ().GetCustomAttributes (
            typeof (System.Reflection.AssemblyFileVersionAttribute), false)[0])).Version);

		const int BUFF_SIZE = 8000;
		const int BUFF_CMD_MULTIPLE = 2;

		Connection m_connection;
		string m_server,
			m_db,
			m_user;
		bool m_bIntSec,
			m_bNewDb = true;
		StreamReader m_sr;
		string m_filename;
		int m_blocklen = 0,
			m_file_idx = 0;

		char [] m_buffer;

		#region Status
		public delegate void StatusChangeEventHandler (object source, StatusChangeEventArgs args);
		public event StatusChangeEventHandler OnStatusChangeHandler;

		void ChangeStatus (string a_str)
		{
			OnStatusChangeHandler (this, new StatusChangeEventArgs (a_str));
		}

		void ChangeStatus (Exception a_ex)
		{
			OnStatusChangeHandler (this, new StatusChangeEventArgs (a_ex));
		}

		#endregion

		public DbInstaller (string a_server, string a_db)
		{
			m_server = a_server;
			m_db = a_db;
			m_bIntSec = true;

			Init ("");
		}

		public DbInstaller (string a_server, string a_db, string a_user, string a_pwd)
		{
			m_server = a_server;
			m_db = a_db;
			m_bIntSec = false;
			m_user = a_user;

			Init (a_pwd);
		}

		void Init (string a_pwd)
		{
			if (m_bIntSec)
			{
				m_connection = new Connection (m_server);
			}
			else
			{
				m_connection = new Connection (m_server, m_user, a_pwd);
			}

			SqlCommand cmd = new SqlCommand ("SET QUOTED_IDENTIFIER OFF", m_connection.SQLConnection);
			cmd.ExecuteNonQuery ();

		}

		public void Close ()
		{
			m_connection.Close ();
			if (m_sr != null) m_sr.Close ();
		}

		public void InitFile (string a_filename)
		{
			m_sr = new StreamReader (a_filename);
			m_buffer = new char [BUFF_SIZE];
			m_filename = a_filename;
		}

		public bool InstallDb ()
		{
			bool ret = true;

			try
			{
				if(m_bNewDb) 
				{
					ExecuteCommand ("CREATE DATABASE "+m_db);
				}
				ExecuteCommand ("USE "+m_db);
				SkipHeader ();
				ProcessFile ();
			}
			catch (Exception ex)
			{
				ChangeStatus (ex);
				ret = false;
			}

			return ret;
		}

		public int CheckExistence ()
		{
			int dbId,
				nObjects,
				ret = 0;

			SqlCommand cmd = new SqlCommand ("Select IsNULL(db_id('"+m_db+"'),0)", m_connection.SQLConnection);

			dbId = (short)cmd.ExecuteScalar ();
			if (dbId > 0)
			{
				m_bNewDb = false;
				ret = -1;
				SqlCommand ncmd = new SqlCommand ("Select count(*) From "
					+m_db+".dbo.sysobjects Where type='U'", m_connection.SQLConnection);
				nObjects = (int)ncmd.ExecuteScalar ();
				if (nObjects > 0) 
				{
					ret = nObjects;
				}
			}

			return ret;
		}

		void SkipHeader ()
		{
			int counter = 0,
				limit = 100;
			string str;

			do
			{
				str = m_sr.ReadLine ();
				counter++;
				if (counter > limit) throw new Exception ("File appears to be corrupted");
			} while (!str.StartsWith ("-- >> OBJECTS <<"));

		}

		void ProcessFile ()
		{
			int buf_idx,
				cmd_len,
				tmp_buf_len = 1,
				tmp_buf_count = 0,
				ret
				;
			bool bEOF,
				bCompleteCmd = true;
			char lchar;

			char [] buff_cmd = new char [BUFF_CMD_MULTIPLE * BUFF_SIZE];
			string cmd;

			do
			{
				m_blocklen = m_sr.Read (m_buffer, 0, BUFF_SIZE);
				m_file_idx += m_blocklen;
				bEOF = (m_blocklen != BUFF_SIZE);

				buf_idx = 0;
				if (!bCompleteCmd) 
				{
					lchar = buff_cmd[tmp_buf_len-1];
					switch (lchar)
					{
						case '\n':
							if (m_buffer[0] == 'G' 
								&& m_buffer[1] == 'O'
								&& (m_buffer [2] == '\r' || m_buffer[2] == ' ') 
								)
							{
								buf_idx = 3;
								bCompleteCmd = true;
								cmd = new string (buff_cmd, 0, tmp_buf_len);
								ret = ExecuteCommand (cmd);
							}
							break;
						case 'G':
							if (buff_cmd[tmp_buf_len-2] == '\n' 
								&& m_buffer[0] == 'O'
								&& (m_buffer [1] == '\r' || m_buffer[1] == ' ') 
								)
							{
								buf_idx = 2;
								bCompleteCmd = true;
								cmd = new string (buff_cmd, 0, tmp_buf_len-1);
								ret = ExecuteCommand (cmd);
							}
							break;
						case 'O':
							if (buff_cmd[tmp_buf_len-3] == '\n' 
								&& buff_cmd[tmp_buf_len-2] == 'G' 
								&& (m_buffer [0] == '\r' || m_buffer[0] == ' ') 
								)
							{
								buf_idx = 1;
								bCompleteCmd = true;
								cmd = new string (buff_cmd, 0, tmp_buf_len-2);
								ret = ExecuteCommand (cmd);
							}
							break;
					}
					// If bCompleteCmd is still false - special case wasn't met
					if (!bCompleteCmd) 
					{
						ParseCommand (buf_idx, out cmd_len, out bCompleteCmd);
						if (!bCompleteCmd)
						{
							// The buff_cmd already holds some data (tmp_buf_count >= 1)
							// We try put the whole buffer here
							if (tmp_buf_count == BUFF_CMD_MULTIPLE)
							{
								throw new Exception ("Command length exceeds maximum allowed");
							}
							tmp_buf_count++;
							Array.Copy (m_buffer, 0, buff_cmd, tmp_buf_len, cmd_len);
							tmp_buf_len += cmd_len;
							continue;
						}
						else 
						{
							if (tmp_buf_len + cmd_len > buff_cmd.Length)
							{
								throw new Exception ("Command length exceeds maximum allowed");
							}
							Array.Copy (m_buffer, 0, buff_cmd, tmp_buf_len, cmd_len);
							tmp_buf_len += cmd_len;
							cmd = new string (buff_cmd, 0, tmp_buf_len);
							ExecuteCommand (cmd);
							buf_idx += cmd_len + 2;
						}
					}
				}

				do
				{
					ParseCommand (buf_idx, out cmd_len, out bCompleteCmd);
					if (bCompleteCmd)
					{
						cmd = new string (m_buffer, buf_idx, cmd_len);
						ret = ExecuteCommand (cmd);
						buf_idx += cmd_len + 2;
					}
					else 
					{
						Array.Copy (m_buffer, buf_idx, buff_cmd, 0, cmd_len);
						tmp_buf_len = cmd_len;
						tmp_buf_count = 1;
					}
				} while (bCompleteCmd);

			} while (!bEOF);

			ChangeStatus ("Installation finished");
		}

		void ParseCommand (int a_start, out int a_length, out bool a_complete)
		{
			int i;

			for (i = a_start + 1; i < m_blocklen - 3; i++)
			{
				if (m_buffer[i] == 'G') 
				{
					if ( (m_buffer[i-1] == '\n') 
						&& m_buffer [i+1] == 'O'
						&& (m_buffer [i+2] == '\r' || m_buffer[i+2] == ' ') 
						)
					{
						a_length = i - a_start;
						a_complete = true;
						return;
					}
				}
			}

			a_length = m_blocklen - a_start;
			a_complete = false;
		}

		int ExecuteCommand (string a_cmd)
		{
			int ret = 0,
				idx;

			idx = (a_cmd.Length > 128) ? 128 : a_cmd.Length;
			ChangeStatus (a_cmd.Substring (0,idx));

			SqlCommand cmd = new SqlCommand (a_cmd, m_connection.SQLConnection);

			try
			{
				ret = cmd.ExecuteNonQuery ();
			}
			catch (SqlException)
			{
				throw;
			}

			return ret;
		}

		public int SetWatson (int a_val)
		{
			int w;
			string s_cmd;

			SqlCommand ecmd = new SqlCommand ("Select ivalue From "+m_db+".dbo.sys_config Where config=1000", 
				m_connection.SQLConnection);

			object obj = ecmd.ExecuteScalar ();
			if (obj != null) 
			{
				w = (int)obj;
				if (w != a_val)
				{
					s_cmd = "Update "+m_db+".dbo.sys_config Set ivalue="+a_val+" Where config=1000";
				}
				else
				{
					return 0;
				}
			}
			else
			{
				s_cmd = "Insert "+m_db+".dbo.sys_config (config,type,comment,ivalue) "
					+"values (1000, 0, 'Data export enabled', "+a_val+")";
			}

			SqlCommand cmd = new SqlCommand (s_cmd, m_connection.SQLConnection);
			int r = cmd.ExecuteNonQuery ();

			return r;
		}
			
		public int GetConfig (ref ArrayList arr, out bool a_watson)
		{
			SqlDataReader dr;
			SqlCommand cmd;
			int i = 0,
				config,
				ivalue;
			short type;
			string comment,
				cvalue,
				cfg_value,
				s_config;

			a_watson = false;

			cmd = new SqlCommand ("Select config,type,IsNULL(comment,''),IsNULL(ivalue,0),IsNULL(cvalue,'') From "+m_db+".dbo.sys_config ",
				m_connection.SQLConnection);

			dr = cmd.ExecuteReader ();
			try
			{
				while (dr.Read ())
				{
					i++;
					s_config = "";
					cfg_value = "";

					config = dr.GetInt32 (0);
					type = dr.GetInt16 (1);
					comment = dr.GetString (2);
					
					if (type == 0)
					{
						ivalue = dr.GetInt32 (3);
						if (config == 1000)
						{
							a_watson = (ivalue == 1);
							cfg_value = (ivalue == 1) ? "true" : "false";
						}
						else
						{
							cfg_value = ivalue.ToString ();
						}
					}
					else if (type == 1)
					{
						cvalue = dr.GetString (4);
						cfg_value = cvalue;
					}

					s_config = comment.PadRight (28, ' ');
					s_config += cfg_value;
					arr.Add (s_config);

				}
			}
			finally
			{
				dr.Close ();
			}

			return i;
		}
	}
}
